# coding=utf-8

from typing import Dict, List, Tuple
import xlrd
import os
from generator.libapvp.convertCBToIPCP import generateCBToIPCPValue
from generator.libapvp.convertIPCPToPA import generateIPCP2PA
from generator.cpp.file_type import CHeader
from generator.cpp.types import CStruct

from utils.util import errorFn, parseCustomData, parseEncodeName, removeSpace
from parse.parse_types import *
from utils.payload import EncodeType

"""
pip3 install xlrd==1.2.0
pip3 install xlutils
"""

def readOneExcle(file:str)->Tuple[Dict[str,EncodeType], List[Module]]:
    excle:xlrd.book.Book = xlrd.open_workbook(file)
    #找到API接口的sheet和type类型sheet
    sheets:List[xlrd.sheet.Sheet] = [excle.sheet_by_name(sheet) for sheet in excle.sheet_names() if str(sheet).lower() == "api" or str(sheet).lower() == "type"]
    modules={}
    types:Dict[str,EncodeType]={}
    for sheet in sheets:
        if sheet.name.lower() == "api":
            #读取API
            for rowIndex in range(1,sheet.nrows):
                module_name = sheet.cell_value(rowIndex, 0)
                if str(module_name).strip() == "":continue
                if str(module_name).strip().lower() == "carconfig" :continue
                service_Id = sheet.cell_value(rowIndex, 1)
                module = modules.get(module_name,None)
                if module is None:
                    module = Module(module_name,str(round(float(service_Id))))
                    modules[module_name]=module
                op_id = str(round(float(sheet.cell_value(rowIndex, 2))))
                api_name = removeSpace(sheet.cell_value(rowIndex, 4))
                cb_type = removeSpace(sheet.cell_value(rowIndex, 5))
                desc = sheet.cell_value(rowIndex, 10)
                isPA = api_name.startswith("PA_") and  cb_type == "NA"
                if isPA :
                    # 这块是一个PA
                    available = removeSpace(sheet.cell_value(rowIndex, 6)) 
                    data = removeSpace(sheet.cell_value(rowIndex, 7))
                    state = removeSpace(sheet.cell_value(rowIndex, 8))
                    format = removeSpace(sheet.cell_value(rowIndex, 9))
                    module.addPA(PA(op_id,api_name,available,data,state,format,desc))
                else:
                    # 这块是一个CB
                    module.addCB(CB(op_id,api_name,cb_type,desc))
        elif sheet.name.lower() == "type":
            #读取类型定义
            for rowIndex in range(1,sheet.nrows):
                type_name = sheet.cell_value(rowIndex, 0)
                if str(type_name).strip() == "":
                    continue
                type_members = sheet.cell_value(rowIndex, 1)
                (name,isStruct) = parseEncodeName(type_name)
                encodeType = EncodeType(name,isEnum=not isStruct,isStruct=isStruct)
                parseCustomData(isStruct,type_members,lambda member:encodeType.addMember(member))
                types[name]=encodeType
    return (types,list(modules.values()))

def parseCBPAExcle(dir:str,encodeTypeDict:Dict[str,EncodeType])->List[Module]:
    custom_types:Dict[str,EncodeType]={}
    modules:Dict[str,Module]={}
    #收集所有的模块以及类型定义
    for (root,_,files) in os.walk(dir):
        for file in [file for file in sorted(files) if not file.startswith(".") and file.endswith(".xlsx")]:
            print("file is {0}".format(os.path.join(root,file)))
            (moudle_types,excle_modules)=readOneExcle(os.path.join(root,file))
            for (name,type) in moudle_types.items():
                if custom_types.get(name,None) is None:
                    custom_types[name]=type
            for module in excle_modules:
                m=modules.get(module.name,None)
                if m == None:
                    modules[module.name]=module
                else:
                    m.cb_apis.extend(module.cb_apis)
                    m.pa_apis.extend(module.pa_apis)
    merge_encodeType_dict = {}
    merge_encodeType_dict.update(encodeTypeDict)
    merge_encodeType_dict.update(custom_types)
    #为每个CB PA添加它的EncodeType
    for module in list(modules.values()):
        for cb in module.cb_apis:
            cb.parsePayload(merge_encodeType_dict,lambda info:errorFn("模块{} {}".format(module.name,info)))
        for pa in module.pa_apis:
            pa.parsePayload(merge_encodeType_dict,lambda info:errorFn("模块{} {}".format(module.name,info)))
    return sorted(list(modules.values()),key=lambda x:x.name.lower())

def genCBPAUtils(modules:List[Module],outDir:str):
    if len(modules) == 0 :
        print("nothing CBPA modules")
        return
    generatorCBPA(modules,outDir)
    generateCBToIPCPValue(modules,outDir)
    generateIPCP2PA(modules,outDir)

def generatorCBPA(modules:List[Module],outDir:str):
    type_exist={}

    type_header = CHeader("PA_CB_FUNC_TYPE")
    for module in modules:
        if module.name.lower() == "carconfig":continue
        m_header = CHeader(module.name)
        m_header.include("pafunctype.h",True)
        
        for cb in module.cb_apis:
            m_header.define(cb.get_op_id_define())
            cb_struct=CStruct("_"+cb.get_send_struct_name(),True,1)
            payload = cb.getPayload()
            if payload.isBaseTypePayload():
                cb_struct.property(payload.getBaseTypePayload().__str__())
            elif payload.isArrayPayload():
                cb_struct.property(payload.getArrayPayload().__str__())
            elif payload.isStructPayload():
                structPayload=payload.getStructPayload()
                cb_struct.property(structPayload.__str__())
                if type_exist.get(structPayload.type_name,True):
                    type_exist[structPayload.type_name]=False
                    cstruct=CStruct(structPayload.type_name,True,4)
                    for member in structPayload.members:
                        cstruct.property(member.__str__())
                    type_header.addMember(cstruct)
            m_header.addMember(cb_struct)
        
        if len(module.pa_apis) > 0:
            m_header.define(module.get_notify_op_define())
            pa_struct = CStruct(module.get_notify_struct_name(),True,1)
            for pa in module.pa_apis:
                payload = pa.getPayload()
                #模块的PA payload只有结构体
                if payload.isStructPayload():
                    structPayload=payload.getStructPayload()
                    if structPayload.name == 'pamculogpanic':
                        pa_struct.property('uint8_t data[1000]')
                    else:
                        pa_struct.property(structPayload.__str__())
                    struct_name =structPayload.type_name
                    if type_exist.get(struct_name,True):
                        type_exist[struct_name]=False
                        cstruct=CStruct(struct_name,True,4)
                        for member in structPayload.members:
                            cstruct.property(member.__str__())

                        type_header.addMember(cstruct)

            m_header.addMember(pa_struct)
        m_header.toFile(os.path.join(outDir,"include/vp"),m_header.file_name+".h")
    type_header.toFile(os.path.join(outDir,"include/vp"),"pafunctype.h")
